// Learn more about Tauri commands at https://tauri.app/v1/guides/features/command

use tauri::{command, AppHandle, Runtime};

use crate::global;
use crate::recording;
use crate::suppa_ftp::Client;
use crate::version;
use core_api::db;

use std::io::Write;
use std::path::Path;
use std::process::{self, Command, Stdio};
type Result<T> = std::result::Result<T, String>;
use base64::prelude::*;
use serde::{Deserialize, Serialize};
use std::collections::HashMap;
use std::sync::{Arc, Mutex};

const EDOG: &str = "edog2505";

pub fn start_edog() -> u32 {
    if let Some(p) = global::write().get_edog() {
        return p.id();
    }

    let my_pid = process::id();
    // 查询hook 状态
    let hook_status = global::read().get_hook_status().to_string(); //true,false
    println!(
        "****start_edog******,hookStatus================={}",
        hook_status
    );

    let cmd = Command::new(EDOG)
        .args(&[my_pid.to_string().as_str(), hook_status.as_str()])
        .stdin(Stdio::piped())
        .stderr(Stdio::piped()) //使用stderr 接收返回消息
        .spawn();
    let mut pid = 0u32;
    if let Ok(p) = cmd {
        pid = p.id();
        global::write().set_edog(Some(p));
    }
    println!("edog==========pid={},my_pid={}", pid, my_pid);
    pid
}

const START_HOOK: &str = "start";
const STOP_HOOK: &str = "stop";

//#[command]
pub fn stop_edog() -> u32 {
    let _ = send_msg_2_edog(STOP_HOOK);
    global::write().stop_edog()
}

#[command]
pub async fn start_hook(cmd: String) -> Result<String> {
    match cmd.as_str() {
        START_HOOK => {
            // 启动新的的
            // 如果已经启动了键盘钩子，需要保持启动键盘钩子
            let pid = start_edog();
            if pid > 0 {
                // 再次尝试发送
                if let Ok(res) = send_msg_2_edog(&cmd) {
                    return Ok(res);
                }
            }
            global::write().set_hook_status(true);
        }
        STOP_HOOK => {
            let _ = stop_edog();
            global::write().set_hook_status(false);
            return Ok("stop".to_string());
        }
        _ => (),
    }

    Err("通讯错误2".to_string())
}

fn send_msg_2_edog(msg: &str) -> Result<String> {
    // 从标准输入输入
    // 从错误输出获取内容
    // 从标准输出获取命令,这个需要监控
    if let Some(process) = global::write().get_edog() {
        if let (Some(stdin), Some(_stderr)) = (process.stdin.as_mut(), process.stderr.as_mut()) {
            // 向子进程发送数据,注意一定要写入换行符
            // 如果子进程已经停止,Failed to write to stdin: Os { code: 232, kind: BrokenPipe, message: "管道正在被关闭。" }
            // 写入换行

            if let Ok(()) = stdin.write_all((msg.to_owned() + "\n").as_bytes()) {
                //let mut reader = BufReader::new(stderr);
                //let mut line = String::new();
                //let _ = reader.read_line(&mut line);
                //println!("Send    : {}", msg);
                //println!("Received: {}", &line);
                // return Ok(line);
                return Ok("ok".to_string());
            }
        }
    }
    Err("通讯错误1".to_string())
}

/// 打开任意一个目录
#[command]
pub fn open_path(path: &Path) -> bool {
    let p = std::process::Command::new("explorer")
        .args(&[path])
        .output()
        .expect("打开目录失败");
    p.status.success()
}

#[command]
#[cfg(target_os = "windows")]
pub fn exec_cmd(cmd: &str, args: Vec<&str>) -> Result<String> {
    use encoding_rs::GBK;
    use std::os::windows::process::CommandExt; //// 需要导入这个 trait 来使用 Windows 特有的扩展
    const CREATE_NO_WINDOW: u32 = 0x08000000; // Windows API 标志

    // start explorer "c:\windows"
    // let p = std::process::Command::new("cmd")
    // .args(&["/C", "start", "explorer", path.to_str().unwrap()])
    // println!("open_path: {:?}", path);

    // let p = std::process::Command::new("cmd")
    // .args(&["/C", "tasklist", "/FO", "CSV", "/NH"]).output();
    // println!("cmd: {:?}", p);

    // let p = std::process::Command::new("tasklist")
    // .args(&[ "/FO", "CSV", "/NH"]).output();
    // println!("cmd: {:?}", p);

    use wait_timeout::ChildExt;

    let cmd_set = std::collections::HashSet::from([
        /*"tasklist", "taskkill", "systeminfo",*/ "wmic", "echo",
    ]);
    if cmd_set.contains(cmd) {
        let task = std::process::Command::new(cmd)
            .creation_flags(CREATE_NO_WINDOW) // 关键点：防止出现黑窗口,这个在release 模式下会出现
            .args(args)
            .stdout(Stdio::piped())
            .stderr(Stdio::piped())
            .spawn();

        match task {
            Ok(mut child) => {
                let secs = std::time::Duration::from_secs(5);
                if child.wait_timeout(secs).unwrap().is_none() {
                    let _ = child.kill();
                    //let _ = child.wait();
                    return Err(format!("command executes timeout!:{}", cmd));
                }
                if let Ok(output) = child.wait_with_output() {
                    if output.status.success() {
                        let (s, _encoding_used, had_errors) = GBK.decode(output.stdout.as_ref());
                        if had_errors {
                            let s = String::from_utf8_lossy(output.stdout.as_ref());
                            return Ok(s.into_owned());
                        }
                        return Ok(s.into_owned());
                    } else {
                        // 出错信息中文乱码
                        let (s, _encoding_used, had_errors) = GBK.decode(output.stderr.as_ref());
                        if had_errors {
                            let s = String::from_utf8_lossy(output.stderr.as_ref());
                            return Ok(s.into_owned());
                        }
                        return Ok(s.into_owned());
                    }
                }
            }
            Err(e) => {
                return Err(format!("command {} error:{}", cmd, e));
            }
        }
    } else {
        return Err(format!("invalid command:{}", cmd));
    }

    Err(format!("unexpected  command  error:{}", cmd))
}

#[command]
pub fn path_exists(path: &Path) -> bool {
    path.exists()
}

pub fn stop_all() {
    stop_edog();
    let _ = recording::stop_recording();
}

#[derive(Clone, Serialize, Deserialize, Debug)]
#[serde(rename_all = "camelCase")]
pub struct UploadArgs {
    key: String,
    src_file: String,
    des_path: String,
    des_filename: String,
    host: String,
    port: u32,
    username: String,
    password: String,
}

#[derive(Clone, Serialize, Deserialize, Debug)]
#[serde(rename_all = "camelCase")]
pub struct UploadStatus {
    key: String,
    status: String,
    percent: u8,
}

fn xor_encrypt_decrypt(data: &[u8], key: u8) -> Vec<u8> {
    data.iter().map(|&b| b ^ key).collect() // 对每个字节进行 XOR 操作
}
fn decode(text: &str) -> String {
    // 分割字符串
    let (encoded_encrypted, key_str) = text.split_at(text.len() - 1);
    // 获取key
    let key = key_str.chars().next().unwrap() as u8;
    // 解密（首先进行 Base64 解码）
    let decoded_encrypted = BASE64_STANDARD
        .decode(&encoded_encrypted)
        .expect("Failed to decode base64");
    let decrypted_bytes = xor_encrypt_decrypt(&decoded_encrypted, key);
    // 将解密后的字节数组转换回字符串
    let decrypted = String::from_utf8(decrypted_bytes).expect("Failed to convert bytes to String");
    decrypted
}

// Channels 设计为快速并提供有序数据。它们在内部用于流式处理操作，例如下载进度、子进程输出和 WebSocket 消息
// https://tauri.app/develop/calling-frontend/#_top
#[command]
pub async fn upload<R: Runtime>(
    _app: AppHandle<R>,
    args: UploadArgs, /* , on_progress: Channel<u8>*/
) -> Result<()> {
    // println!("upload args: {:?} ", args,);
    let arg_key = args.key.clone();

    // port 没有加密
    // let host= "10.55.55.55".to_string(); //decode(&args.host);
    // let username="yinxiaolian".to_string(); // decode(&args.username);
    // let password="666666".to_string();// decode(&args.password);

    let host = decode(&args.host);
    let username = decode(&args.username);
    let password = decode(&args.password);
    //log::info!("ftp into: {}:{}{}{} ", &host,args.port, &username, &password);

    let mut ftp = Client::new(host, args.port, username, password);
    ftp.connect().map_err(|e| {
        //global::write().add_status(arg_key.clone(), 250);
        //db::send_msg(EVENT_UPLOAD, arg_key.as_str(), PERCENT_ERROR);
        let msg = "无法连接到文件服务器!";
        let _ = global::read()
            .get_message_manager()
            .send_msg(db::Message::new(
                db::MsgFlag::UPLOAD,
                db::MsgStatus::ERROR,
                arg_key.clone(),
                msg.to_string(),
            ));
        log::error!("{} {}", msg, e);
        msg.to_string()
    })?;
    ftp.login().map_err(|e| {
        //global::write().add_status(arg_key.clone(), 250);
        //db::send_msg(EVENT_UPLOAD, arg_key.as_str(), PERCENT_ERROR);
        let msg = "无法登录到文件服务器!";
        let _ = global::read()
            .get_message_manager()
            .send_msg(db::Message::new(
                db::MsgFlag::UPLOAD,
                db::MsgStatus::ERROR,
                arg_key.clone(),
                msg.to_string(),
            ));
        log::error!("{} {}", msg, e);
        msg.to_string()
    })?;
    //let app1 = app.clone();

    ftp.upload(
        Path::new(&args.src_file),
        &args.des_path,
        &args.des_filename,
        move |percent| {
            // let _ = on_progress.send(percent);
            // let _ = app.emit(
            //     EVENT_UPLOAD,
            //     UploadStatus {
            //         key: args.key.clone(),
            //         status: "上传中".to_string(),
            //         percent,
            //     },
            // );
            if percent == 0 {
                let _ = global::read()
                    .get_message_manager()
                    .send_msg(db::Message::new(
                        db::MsgFlag::UPLOAD,
                        db::MsgStatus::START,
                        args.key.clone(),
                        "开始上传".to_string(),
                    ));
            } else if percent % 10 == 0 {
                let _ = global::read()
                    .get_message_manager()
                    .send_msg(db::Message::new(
                        db::MsgFlag::UPLOAD,
                        db::MsgStatus::PROGRESS,
                        args.key.clone(),
                        percent.to_string(),
                    ));
                if percent >= 100 {
                    let _ = global::read()
                        .get_message_manager()
                        .send_msg(db::Message::new(
                            db::MsgFlag::UPLOAD,
                            db::MsgStatus::SUCCESS,
                            args.key.clone(),
                            "上传完成".to_string(),
                        ));
                }
            }
        },
    )
    .map_err(move |e| {
        //println!("============{:?}",e);
        // let _ = app1.emit(
        //     EVENT_UPLOAD,
        //     UploadStatus {
        //         key: arg_key.clone(),
        //         status: format!("error{:?}", e),
        //         percent: 250,
        //     },
        // );
        // global::write().add_status(arg_key.clone(), 250);
        match e {
            suppaftp::types::FtpError::UnexpectedResponse(res) => {
                let body = String::from_utf8_lossy(&res.body);
                let msg = "上传文件发生错误1";
                let _ = global::read()
                    .get_message_manager()
                    .send_msg(db::Message::new(
                        db::MsgFlag::UPLOAD,
                        db::MsgStatus::ERROR,
                        arg_key.clone(),
                        msg.to_string(),
                    ));
                log::error!("{} {}", msg, body);
                msg.to_string()
            }
            _ => {
                let _ = global::read()
                    .get_message_manager()
                    .send_msg(db::Message::new(
                        db::MsgFlag::UPLOAD,
                        db::MsgStatus::ERROR,
                        arg_key.clone(),
                        e.to_string(),
                    ));
                format!("上传文件发生错误2!{:?}", e)
            }
        }
    })?;

    Ok(())
}

#[derive(Clone, Serialize, Deserialize, Debug)]
#[serde(rename_all = "camelCase")]
pub struct SysInfo {
    total_memory_gb: u64,
    cpus: usize,
    os_version: String,
    host_name: String,
    disk: String,
    total_space_gb: u64,
    available_space_gb: u64,
}

/// 获取一些系统信息
#[command]
pub fn get_sysinfo(path: &Path) -> Result<SysInfo> {
    use sysinfo::{Disks, System};

    //获取路径的第一部分，在windows 下表示盘符
    let disk = get_first_component(path);
    let disk = disk.or(Some("C:".to_string())).unwrap();

    let mut sys = System::new_all();

    // 更新系统信息
    sys.refresh_all();

    let disks = Disks::new_with_refreshed_list();

    let mut si = SysInfo {
        total_memory_gb: sys.total_memory() >> 30,
        cpus: sys.cpus().len(), //逻辑处理器
        os_version: System::os_version().unwrap(),
        host_name: System::host_name().unwrap(),
        disk: disk.clone(),
        total_space_gb: 0,
        available_space_gb: 0,
    };
    for d in &disks {
        if get_first_component(d.mount_point())
            .unwrap()
            .starts_with(&disk)
        {
            si.total_space_gb = d.total_space() >> 30;
            si.available_space_gb = d.available_space() >> 30;
        }
    }
    Ok(si)
}

fn get_first_component(path: &Path) -> Option<String> {
    path.components().next().map(|component| {
        component
            .as_os_str()
            .to_string_lossy()
            .to_string()
            .to_uppercase()
    })
}

#[command]
pub fn get_status(key: Option<String>) -> Arc<Mutex<HashMap<String, u8>>> {
    global::write().get_status(key)
}

#[tauri::command]
pub fn get_exe_dir() -> String {
    std::env::current_exe()
        .ok()
        .and_then(|path| path.parent().map(|p| p.display().to_string()))
        .unwrap_or_else(|| "无法获取可执行文件目录".into())
}

#[tauri::command]
pub fn get_desktop_count() -> Result<u32> {
    //    use libloading::{Library, Symbol};
    //    // 加载 DLL（需确保文件 VirtualDesktopAccessor.dll 和可执行文件在同一目录）
    //    unsafe {
    //        let lib = Library::new("VirtualDesktopAccessor.dll").unwrap();
    //        let func: Symbol<unsafe extern "stdcall" fn() -> i32> = lib.get(b"GetDesktopCount").unwrap();
    //        let count = func();
    //        log::info!("当前虚拟桌面数量: {}", count);
    //    }

    //  Windows 10 1903 (Build 18362) 或更高版本
    // 在内网windows10 上报错 COM 错误码 0x80004002 的含义是：E_NOINTERFACE – "No such interface supported"
    match winvd::get_desktop_count() {
        Ok(count) => Ok(count),
        Err(err) => {
            //log::error!("{:?}", &err);
            Err(format!("{:?}", err))
        }
    }
}

#[tauri::command]
pub fn check_env() -> Result<bool> {
    let r = is_virtual_machine() || is_remote_desktop_session();
    Ok(!r)
}

fn is_remote_desktop_session() -> bool {
    // SM_REMOTESESSION = 0x1000
    // 这个函数是起作用的，能够检测远程桌面
    let is_remote_desktop = unsafe { winapi::um::winuser::GetSystemMetrics(0x1000) != 0 };
    log::info!("is_remote_desktop: {}", is_remote_desktop);
    return is_remote_desktop;
}

fn is_virtual_machine() -> bool {
    let cpuid = raw_cpuid::CpuId::new();

    if let Some(vf) = cpuid.get_vendor_info() {
        log::info!("CPU manufacturer: {}", vf.as_str());
    }

    if let Some(brand) = cpuid.get_processor_brand_string() {
        log::info!("CPU brand: {}", brand.as_str());
        false
    } else {
        false
    }
}

use std::ffi::OsString;

#[tauri::command]
pub fn log_all_process() -> Vec<String> {
    let ps = recording::get_all_process();
    log::info!("{}", ps.join(";"));
    return ps;
}

/// 判断给定进程名列表中是否有任意一个正在运行（大小写不敏感）
#[tauri::command]
pub fn check_process(target_names: Vec<String>) -> Vec<(String, u8)> {
    recording::check_process(target_names)
}

#[tauri::command]
pub fn kill_process(target_names: Vec<String>) -> Vec<(OsString, u8)> {
    recording::kill_process_by_names(target_names)
}

#[tauri::command]
pub fn log(msg: &str, level: &str) {
    match level {
        "info" => {
            log::info!("{}", msg);
        }
        "error" => {
            log::error!("{}", msg);
        }
        _ => (),
    }
}
#[tauri::command]
pub fn get_msg() -> Result<Vec<db::Message>> {
    // db::send_msg("key_aa", "val_aa");
    global::read().get_message_manager().get_message()
}

#[tauri::command]
pub fn ack_msg(id: u32) -> Result<u32> {
    let _ = global::read().get_message_manager().send_delete(id);
    Ok(id)
}

#[tauri::command]
pub fn get_version() -> String {
    version::VERSION.to_string()
}
